﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using BacteriaSimulation.Simulation;
using System.Threading;
using BacteriaSimulation.GUI;
using System.Windows.Media.Animation;

namespace BacteriaSimulation
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        protected delegate void updateDelegateEventHandler(SimulationUpdateEventArgs args);
        protected delegate void deathDelegateEventHandler(BacteriumUpdateEventArgs args);
        protected SimulationEnvironment Env { get; set; }
        protected Simulation.Simulation Simulation { get; set; }
        protected Thread simulationThread;

        protected Point clippingRectOrigin;
        protected Rectangle clippingRectangle;
        protected List<Line> gridLines;

        protected Dictionary<Bacterium, BacteriumView> viewDictonary;

        public MainWindow()
        {
            InitializeComponent();
            this.Closing += MainWindow_Closing;
            this.SimulationCanvas.MouseLeftButtonDown += SimulationCanvas_MouseLeftButtonDown;
            this.chkDrawGrid.Unchecked += chkDrawGrid_Checked;

            cmbDetectCollision.Items.Add("true");
            cmbDetectCollision.Items.Add("false");

            txtMaxEnergy.Text = txtCannibalMaxEnergy.Text = SimulationEnvironment.MAX_ENERGY.ToString();
            txtInitialEnergy.Text = txtCannibalInitialEnergy.Text = SimulationEnvironment.INITIAL_ENERGY.ToString();
            txtMaxSpeedComponent.Text = txtCannibalMaxSpeedComponent.Text = SimulationEnvironment.MAX_SPEED_COMPONENT.ToString();
            txtMaxCyanoCount.Text = SimulationEnvironment.MAX_BACTERIA_COUNT.ToString();
            txtCyanoEnergyInc.Text = txtCannibalEnergyInc.Text = SimulationEnvironment.ENERGY_INCREMENT.ToString();

            this.gridLines = new List<Line>();
        }

        void SimulationCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Point origin = e.GetPosition(SimulationCanvas);
            clippingRectOrigin = origin;
            this.SimulationCanvas.MouseMove += SimulationCanvas_MouseMove;
            this.SimulationCanvas.MouseLeftButtonUp += SimulationCanvas_MouseLeftButtonUp;
            if(SimulationCanvas.Children.Contains(clippingRectangle))
            {
                SimulationCanvas.Children.Remove(clippingRectangle);
            }
            this.clippingRectangle = new Rectangle();
        }

        void SimulationCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            try
            {
                Point mousePosition = e.GetPosition(SimulationCanvas);
                Point rectangleOrigin = new Point(clippingRectOrigin.X, clippingRectOrigin.Y);
                if(mousePosition.X < clippingRectOrigin.X)
                {
                    rectangleOrigin.X = mousePosition.X;
                }
                if (mousePosition.Y < clippingRectOrigin.Y)
                {
                    rectangleOrigin.Y = mousePosition.Y;
                }

                Double width = Math.Abs( mousePosition.X - clippingRectOrigin.X );
                Double height = Math.Abs( mousePosition.Y - clippingRectOrigin.Y);

                clippingRectangle.Stroke = Brushes.Black;
                Canvas.SetLeft(clippingRectangle, rectangleOrigin.X);
                Canvas.SetTop(clippingRectangle, rectangleOrigin.Y);
                clippingRectangle.Width = width;
                clippingRectangle.Height = height;
                if(!SimulationCanvas.Children.Contains(clippingRectangle))
                {
                    SimulationCanvas.Children.Add(clippingRectangle);
                }
            }
            catch (Exception ex)
            {
                System.Console.WriteLine(ex.Message);
            }
        }

        void SimulationCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            this.SimulationCanvas.MouseMove -= SimulationCanvas_MouseMove;
            this.SimulationCanvas.Children.Remove(clippingRectangle);
            KillRangeCommand cmd = new KillRangeCommand(Canvas.GetLeft(clippingRectangle), Canvas.GetTop(clippingRectangle), clippingRectangle.Width, clippingRectangle.Height);
            Simulation.EnqueueCommand(cmd);
        }

        void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (this.Simulation != null)
            {
                this.Simulation.EnqueueCommand(new SimulationCommand("stop"));
                simulationThread.Abort();
            }
        }

        private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                SimulationEnvironment env = new SimulationEnvironment(this.SimulationCanvas.Width, this.SimulationCanvas.Height);
                this.Env = env;
                // collects initialization parameters
                try
                {
                    env.MaxEnergy = double.Parse(txtMaxEnergy.Text);
                    env.InitialEnergy = double.Parse(txtInitialEnergy.Text);
                    env.MaxSpeedComponent = double.Parse(txtMaxSpeedComponent.Text);
                    env.MaxBacteriaCount = int.Parse(txtMaxCyanoCount.Text);
                    env.EnergyIncrement = double.Parse(txtCyanoEnergyInc.Text);
                    env.DetectBacteriaCollision = Boolean.Parse(cmbDetectCollision.SelectedValue.ToString());
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }

                int rowCount = int.Parse(rowCountTxt.Text);
                int columnCount = int.Parse(columnCountTxt.Text);

                Simulation.Simulation simulation = new Simulation.Simulation(env, rowCount, columnCount);
                simulation.SimulationUpdate += simulation_SimulationUpdate;

                viewDictonary = new Dictionary<Bacterium, BacteriumView>();

                Simulation = simulation;
                System.Threading.Thread thread = new System.Threading.Thread(simulation.SimulationThreadStart);
                this.simulationThread = thread;
                this.StartButton.IsEnabled = false;
                this.StopButton.IsEnabled = true;

                if ((bool)chkDrawGrid.IsChecked)
                {
                    DrawGrid();
                }

                thread.Start();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        protected void DrawGrid()
        {
            ClearGrid();
            this.gridLines.Clear();

            double cellWidth = Env.Width / Simulation.Grid.ColumnCount;
            double cellHeight = Env.Height / Simulation.Grid.RowCount;
            // Draws vertical lines
            double currentX = cellWidth;
            while (currentX <= Env.Width)
            {
                Line vertical = new Line();
                vertical.Stroke = Brushes.Black;
                vertical.X1 = currentX;
                vertical.X2 = currentX;
                vertical.Y1 = 0;
                vertical.Y2 = Env.Height;
                SimulationCanvas.Children.Add(vertical);
                this.gridLines.Add(vertical);

                currentX += cellWidth;
            }
            // Draws horizontal lines
            double currentY = cellHeight;
            while (currentY <= Env.Height)
            {
                Line horizontal = new Line();
                horizontal.Stroke = Brushes.Black;
                horizontal.X1 = 0;
                horizontal.X2 = Env.Width;
                horizontal.Y1 = currentY;
                horizontal.Y2 = currentY;
                SimulationCanvas.Children.Add(horizontal);
                this.gridLines.Add(horizontal);

                currentY += cellHeight;
            }
        }

        protected void ClearGrid()
        {
            foreach (Line line in gridLines)
            {
                SimulationCanvas.Children.Remove(line);
            }
            gridLines.Clear();
        }

        void simulation_SimulationUpdate(SimulationUpdateEventArgs args)
        {
            Object[] functionArgs = new Object[1];
            functionArgs[0] = args;
            updateDelegateEventHandler d = UpdateView;
            this.Dispatcher.Invoke(d, System.Windows.Threading.DispatcherPriority.Background, functionArgs);
        }

        public void UpdateView(SimulationUpdateEventArgs args)
        {
            foreach (Bacterium b in args.Bacteria)
            {
                UpdateBacteriumView(b);
            }
            this.cyanoCountLabel.Content = args.CyanobacteriaCount;
            this.cannibalCountLabel.Content = args.CannibalBacteriaCount;
            //Simulation.EnqueueCommand(new SimulationCommand("resume"));
        }

        protected void UpdateBacteriumView(Bacterium bacterium)
        {
            BacteriumView view = null;

            if (!this.viewDictonary.ContainsKey(bacterium) )
            {
                if (bacterium.State != BacteriumState.DEAD)
                {
                    Brush fill = null;
                    if (bacterium.Strategy is CannibalBacteriumStrategy)
                    {
                        fill = Brushes.Red;
                    }
                    view = AddBacteriumView(bacterium, fill);
                    bacterium.Death += bact_Death;
                }
                else
                {
                    return;
                }
            }
            else
            {
                if (bacterium.State == BacteriumState.DEAD)
                {
                    RemoveBacteriumView(bacterium);
                    return;
                }
                else
                {
                    view = this.viewDictonary[bacterium];
                }
            }
            //view.Grow((bacterium.Width - view.Width), (bacterium.Height - view.Height));
            view.AdjustSize(bacterium.Width, bacterium.Height);

            Canvas.SetLeft(view, bacterium.X);
            Canvas.SetTop(view, bacterium.Y);
            //this.SimulationCanvas.InvalidateVisual();

            /////////////////////////////////////////////////////////////////////////////////////
            //Storyboard storyb = new Storyboard();

            //Duration dur = new Duration(TimeSpan.FromMilliseconds(Simulation.SimulationStepInterval));
            //DoubleAnimation leftAnimation = new DoubleAnimation(bacterium.X, dur);
            //DoubleAnimation topAnimation = new DoubleAnimation(bacterium.Y, dur);

            //storyb.Children.Add(leftAnimation);
            //storyb.Children.Add(topAnimation);

            //Storyboard.SetTarget(leftAnimation, view);
            //Storyboard.SetTarget(topAnimation, view);

            //Storyboard.SetTargetProperty(leftAnimation, new PropertyPath(Canvas.LeftProperty));
            //Storyboard.SetTargetProperty(topAnimation, new PropertyPath(Canvas.TopProperty));

            //storyb.Begin();
            //////////////////////////////////////////////////////////////////////////////////////
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            if (Simulation != null)
            {
                Simulation.EnqueueCommand(new SimulationCommand("pause"));
                PauseButton.IsEnabled = false;
                ResumeButton.IsEnabled = true;
            }
        }

        private void ResumeButton_Click(object sender, RoutedEventArgs e)
        {
            if (Simulation != null)
            {
                Simulation.EnqueueCommand(new SimulationCommand("resume"));
                PauseButton.IsEnabled = true;
                ResumeButton.IsEnabled = false;
            }
        }

        private void StopButton_Click(object sender, RoutedEventArgs e)
        {
            if (Simulation != null)
            {
                Simulation.EnqueueCommand(new SimulationCommand("stop"));
                this.SimulationCanvas.Children.Clear();
                this.viewDictonary.Clear();
            }
                StartButton.IsEnabled = true;
                StopButton.IsEnabled = false;
        }

        private void AddButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // Random generation of the initial speed vector:
                //Random r = new Random();
                //Double xComp = r.NextDouble() * SimulationEnvironment.MAX_SPEED_COMPONENT;
                //Double yComp = r.NextDouble() * SimulationEnvironment.MAX_SPEED_COMPONENT;
                Vector2D speed = Vector2D.GetRandomVector2D(double.Parse(txtMaxSpeedComponent.Text)); //new Vector2D(xComp, yComp);

                Bacterium bact = new Bacterium(new SimulationPoint(0, 0),
                    speed, double.Parse(txtInitialEnergy.Text), new CyanobacteriumSimulationStrategy(), double.Parse(txtMaxEnergy.Text), double.Parse(txtCyanoEnergyInc.Text));
                SimulationCommand cmd = new AddCommand(bact);
                AddBacteriumView(bact, null);
                Simulation.EnqueueCommand(cmd);
                bact.Death += bact_Death;
                //this.SimulationCanvas.InvalidateVisual();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        void bact_Death(BacteriumUpdateEventArgs args)
        {
            Object[] functionArgs = new Object[1];
            functionArgs[0] = args;
            deathDelegateEventHandler d = BacteriumDeath;
            this.Dispatcher.BeginInvoke(d, functionArgs);
        }

        protected void BacteriumDeath(BacteriumUpdateEventArgs args)
        {
            if (viewDictonary.ContainsKey(args.SourceBacterium))
            {
                BacteriumView view = viewDictonary[args.SourceBacterium];
                this.viewDictonary.Remove(args.SourceBacterium);
                this.SimulationCanvas.Children.Remove(view);
            }
        }

        protected BacteriumView AddBacteriumView(Bacterium bacterium, Brush fill)
        {
            BacteriumView view = new BacteriumView();
            if (fill != null)
            {
                view.internalEllipse.Fill = fill;
                view.internalEllipse.Stroke = fill;
            }
            view.Width = bacterium.Width;
            view.Height = bacterium.Height;
            this.SimulationCanvas.Children.Add(view);
            Canvas.SetLeft(view, bacterium.X);
            Canvas.SetTop(view, bacterium.Y);
            viewDictonary.Add(bacterium, view);

            return view;
        }

        protected void RemoveBacteriumView(Bacterium bact)
        {
            if (viewDictonary.ContainsKey(bact))
            {
                BacteriumView view = viewDictonary[bact];
                SimulationCanvas.Children.Remove(view);
                viewDictonary.Remove(bact);
            }
        }

        private void AddCannibalBacteriumButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Vector2D speed = Vector2D.GetRandomVector2D(double.Parse(txtCannibalMaxSpeedComponent.Text)); //new Vector2D(xComp, yComp);

                Bacterium bact = new Bacterium(new SimulationPoint(0, 0), speed, double.Parse(txtCannibalInitialEnergy.Text), 
                    new CannibalBacteriumStrategy(), double.Parse(txtCannibalMaxEnergy.Text), double.Parse(txtCannibalEnergyInc.Text));
                SimulationCommand cmd = new AddCommand(bact);
                AddBacteriumView(bact, Brushes.Red);
                Simulation.EnqueueCommand(cmd);
                bact.Death += bact_Death;
                //this.SimulationCanvas.InvalidateVisual();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.StackTrace, ex.Message, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void chkDrawGrid_Checked(object sender, RoutedEventArgs e)
        {
            if (this.Simulation != null)
            {
                if ((bool)chkDrawGrid.IsChecked)
                {
                    DrawGrid();
                }
                else
                {
                    ClearGrid();
                }
            }
        }

        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            if (this.Simulation != null)
            {
                Simulation.EnqueueCommand(new SimulationCommand("dumpIndex"));
            }
        }

    }
}
